#ifndef PARSER_BASE_H
#define PARSER_BASE_H

#include <string>
#include <regex>
#include <boost\algorithm\string.hpp>
#include <boost\date_time.hpp>
#include <stdexcept>
#include "ParserDateTime.h"

using namespace std;
using namespace boost::posix_time;
using namespace boost::gregorian;

/**
 * This forms the base class of the parser. Commands are entered fed into
 * objects of this class. If this class cannot parse the command, it will 
 * pass the request on to the next class in the chain of responsibility.
 *
 @author Ling Chun Kai
*/
class ParserBase {
public:
	
	// The following are the CommandType's which are currently supported
	// It will be populated as additional functionality is added
	enum CommandType {
		ADD_FLOATING_TASK,
		ADD_DEADLINE_TASK,
		ADD_TIMED_TASK,
		LIST_ALL,
		LIST_MONTH,
		LIST_DAY,
		LIST_WEEK,
		LIST_SEARCH_TERM,
		SORT_TIME,
		SORT_ALPHABET,
		SORT_FLAG,
		SORT_COMPLETED,
		EDIT_START_TIME,
		EDIT_START_DATETIME,
		EDIT_END_TIME,
		EDIT_END_DATETIME,
		EDIT_RENAME,
		DELETE_SEARCHTERM,
		DELETE_TASKID,
		DELETE_DONE,
		DONE_TASKID,
		DONE_SEARCHTERM,
		INVALID
	};

	// This structure defines the return type from which users may access 
	// the parsed attributes of the command.
	// In the current implementation, each instruction has its own set of 
	// members, to allow for all parsed commands to have a single return type.
	// However, within each instruction, there could be several variables
	// which are shared. For example, taskNameAdd is used for all variations
	// of the instruction "add"
	struct PARSED_COMMAND {
		enum Error{NO_ERROR, 
				   ERROR_BAD_INSTRUCTION,
				   ERROR_FORMAT_GENERAL, 
				   ERROR_FORMAT_ADD,
				   ERROR_FORMAT_DATE,
				   ERROR_FORMAT_SORT,
				   ERROR_FORMAT_EDIT,
				   ERROR_FORMAT_DELETE,
				   ERROR_FORMAT_DONE,
				   ERROR_INVALID_TASK_INDEX
				  };

		CommandType commandType;
		Error errorType;

		// Used for add
		string addTaskName;
		ptime addStartTime;
		ptime addEndTime;
		
		// Used for list
		string listSearchTerm;

		// Used for edit
		int editTaskID;
		string editTaskName; // Name which we rename into
		// in the following 4 situations, we differentiate between "TIME" and DATE
		time_duration editStartTime;
		ptime editStartDateTime;
		time_duration editEndTime;
		ptime editEndDateTime;

		// Used for delete
		int deleteTaskID;
		string deleteSearchTerm;

		// Used for done
		int doneTaskID;
		string doneSearchTerm;
	};
	
	// Constructs an instance of the ParserBase class. Sets the sucessor
	// of this instance to be _nextParser
	ParserBase(ParserBase* _nextParser);
	
	// Returns a parsed command containing the parsed information.
	PARSED_COMMAND parseUserCommand(string userCommand);

	// The next handler to have a shot at parsing this command should this
	// instance of parser fail to accurately parse the given command
	ParserBase* nextParser;

protected:
	// This method attempts to parse the user command, and returns true
	// if successful. It also reads fills the parsdCommand struct with
	// relevenat data if and only if the parsing was successful.
	virtual bool tryParse(string userCommand, PARSED_COMMAND& parsedCommand) = 0;

	// Date/Time parser. This method reads in a single string and returns
	// a boost::date_time::posix_time coressponding to the string input
	// The input may or may bit include the time.
	ptime parseDateTime(const string& timeStr);

	// This method parses a string containing the time, and returns a 
	// time_duration object coressponding to that time. The time passed
	// should be < 24 hours. It will return not_a_date_time() if there is
	// an unrecognized syntax, or if the time parsed is >= 24 hrs.
	time_duration parseTime(const string& timeStr);

	// Parses a date, without the time.
	ptime parseDate(const string& timeStr);

	// This method aids in extracting a task ID from a particular field
	// from matching arguments. Returns the extracted taskID. 
	// This will raise an assertion if the matchingArgument is NOT
	// a valid number
	int getTaskIDFromArgument(string argument);
private:
	static const int NUM_HOURS_IN_DAY = 24;
	static ParserDateTime parserDateTime;
};

#endif